// Copyright Epic Games, Inc. All Rights Reserved.

#include "AsyncAction_CommonUserInitialize.h"

#include "GenericPlatform/GenericPlatformInputDeviceMapper.h"
#include "TimerManager.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_CommonUserInitialize)

UAsyncAction_CommonUserInitialize* UAsyncAction_CommonUserInitialize::InitializeForLocalPlay(UCommonUserSubsystem* Target, int32 LocalPlayerIndex, FInputDeviceId PrimaryInputDevice, bool bCanUseGuestLogin)
{
	if (!PrimaryInputDevice.IsValid())
	{
		// Set to default device
		PrimaryInputDevice = IPlatformInputDeviceMapper::Get().GetDefaultInputDevice();
	}

	UAsyncAction_CommonUserInitialize* Action = NewObject<UAsyncAction_CommonUserInitialize>();
	/**
	 * Call to globally register this object with a game instance, it will not be destroyed until SetReadyToDestroy is called
	 * This allows having an action stay alive until SetReadyToDestroy is manually called, allowing it to be used inside loops or if the calling BP goes away
	 */

	/**
	 * RegisterWithGameInstance: 
	 * 在游戏实例中全局注册这个对象使调用，它将在直到调用SetReadyToDestroy时才会被销毁。
	 */
	Action->RegisterWithGameInstance(Target);

	if (Target && Action->IsRegistered())
	{
		Action->Subsystem = Target;
		
		Action->Params.RequestedPrivilege = ECommonUserPrivilege::CanPlay;
		Action->Params.LocalPlayerIndex = LocalPlayerIndex;
		Action->Params.PrimaryInputDevice = PrimaryInputDevice;
		Action->Params.bCanUseGuestLogin = bCanUseGuestLogin;
		Action->Params.bCanCreateNewLocalPlayer = true;
	}
	else
	{
		/** Call when the action is completely done, this makes the action free to delete, and will unregister it with the game instance */

		/** SetReadyToDestroy: 当此行为完成时调用，这确保行为免费去删除，并且在游戏实例中取消注册。 */
		Action->SetReadyToDestroy();
	}

	return Action;
}

UAsyncAction_CommonUserInitialize* UAsyncAction_CommonUserInitialize::LoginForOnlinePlay(UCommonUserSubsystem* Target, int32 LocalPlayerIndex)
{
	UAsyncAction_CommonUserInitialize* Action = NewObject<UAsyncAction_CommonUserInitialize>();

	Action->RegisterWithGameInstance(Target);

	/** Returns true if this action is registered with a valid game instance */

	/** IsRegistered: 如果这个行为被注册在有效的游戏实例中则返回True。 */
	if (Target && Action->IsRegistered())
	{
		Action->Subsystem = Target;
		
		Action->Params.RequestedPrivilege = ECommonUserPrivilege::CanPlayOnline;
		Action->Params.LocalPlayerIndex = LocalPlayerIndex;
		Action->Params.bCanCreateNewLocalPlayer = false;
	}
	else
	{
		Action->SetReadyToDestroy();
	}

	return Action;
}

void UAsyncAction_CommonUserInitialize::HandleFailure()
{
	const UCommonUserInfo* UserInfo = nullptr;
	if (Subsystem.IsValid())
	{
		UserInfo = Subsystem->GetUserInfoForLocalPlayerIndex(Params.LocalPlayerIndex);
	}
	HandleInitializationComplete(UserInfo, false, NSLOCTEXT("CommonUser", "LoginFailedEarly", "Unable to start login process"), Params.RequestedPrivilege, Params.OnlineContext);
}

void UAsyncAction_CommonUserInitialize::HandleInitializationComplete(const UCommonUserInfo* UserInfo, bool bSuccess, FText Error, ECommonUserPrivilege RequestedPrivilege, ECommonUserOnlineContext OnlineContext)
{
	/** This should be called prior to broadcasting delegates back into the event graph, this ensures the action is still valid */

	/** ShouldBroadcastDelegates: 这应该在去广播代理到事件图标之前被调用，这确保这个行为是有效的。 */
	if (ShouldBroadcastDelegates())
	{
		OnInitializationComplete.Broadcast(UserInfo, bSuccess, Error, RequestedPrivilege, OnlineContext);
	}

	SetReadyToDestroy();
}

void UAsyncAction_CommonUserInitialize::Activate()
{
	if (Subsystem.IsValid())
	{
		Params.OnUserInitializeComplete.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(UAsyncAction_CommonUserInitialize, HandleInitializationComplete));
		bool bSuccess = Subsystem->TryToInitializeUser(Params);

		if (!bSuccess)
		{
			// Call failure next frame
			FTimerManager* TimerManager = GetTimerManager();
			
			if (TimerManager)
			{

				/**
				 * Sets a timer to call the given native function on the next tick.
				 *
				 * @param inObj					Object to call the timer function on.
				 * @param inTimerMethod			Method to call when timer fires.
				 */

				/**
				 * SetTimerForNextTick: 设置一个定时器，在下一帧去调用给定的原始函数。
				 */
				TimerManager->SetTimerForNextTick(FTimerDelegate::CreateUObject(this, &UAsyncAction_CommonUserInitialize::HandleFailure));
			}
		}
	}
	else
	{
		SetReadyToDestroy();
	}	
}

